#include "frame.h"
#include "util.h"
#include "assem.h"

#define REG_FORPARAM     4

Temp_tempList specialRegs;     // special regs like RV, FP, SP, etc
Temp_tempList argRegs;         // regs used to post parameters
Temp_tempList calleeSaves;     // regs func callee must protect
Temp_tempList callerSaves;     // regs func caller must protect

static F_access InFrame(int offset);
static F_access InReg(Temp_temp reg);

struct F_frame_
{
    unsigned length;
    unsigned paramCount;
    Temp_label     name;
    F_accessList     list;
    F_accessList    paramBegin; // the end of the parameters
};


int F_getRegNum()
{
    return 4;
};


Temp_temp F_RV()
{
    static Temp_temp temp = NULL;
    if (!temp)
    {
        temp = Temp_newtemp();
        Temp_enter(Temp_name(), temp, "RV");
    }
    return temp;
};

Temp_temp F_FP()
{
    static Temp_temp temp = NULL; // temp approch
    if (!temp)
    {
        temp = Temp_newtemp();
        Temp_enter(Temp_name(), temp, "FP");
    }
    return temp;
};

Temp_temp F_SP()
{
    static Temp_temp temp = NULL;
    if (!temp)
    {
        temp = Temp_newtemp();
        Temp_enter(Temp_name(), temp, "SP");
    }
    return temp;
}

Temp_temp eax, ebx, ecx, edx;
Temp_temp ebp, esp;

Temp_tempList F_registers()
{
    static Temp_tempList registers = NULL;
    if (registers)
        return registers;
    else
    {
        eax = Temp_newtemp();    ebx = Temp_newtemp();
        ecx = Temp_newtemp();    edx = Temp_newtemp();
        ebp = Temp_newtemp();    esp = Temp_newtemp();

        Temp_enter(Temp_name(), eax, "EAX");
        Temp_enter(Temp_name(), ebx, "EBX");
        Temp_enter(Temp_name(), ecx, "ECX");
        Temp_enter(Temp_name(), edx, "EDX");
        Temp_enter(Temp_name(), ebp, "EBP");
        Temp_enter(Temp_name(), esp, "ESP");
        registers = Temp_TempList(eax, 
            Temp_TempList(ebx, 
            Temp_TempList(ecx, 
            Temp_TempList(edx,
            Temp_TempList(ebp, 
            Temp_TempList(esp, NULL))))));        // x86 has not zero register
        return registers;
    }
}

int F_singleSize()
{
#ifdef DOUBLE_WORD
    return 2;
#else
    return 1;
#endif
};

// Generate instruction to access variable
T_exp F_Exp(F_access access, T_exp framePtr)
{
    if (access->kind == inFrame)
    {
        if (access->u.offset != 0)
            return T_Mem(T_Binop(T_plus,
                T_Temp(framePtr->u.TEMP),
                T_Const(access->u.offset)));
        else
            return T_Mem(T_Temp(
                framePtr->u.TEMP));
    }
    else
        return T_Mem(T_Temp(access->u.reg));
}

T_exp F_externalCall(string s, T_expList args)
{
    // static link is posted in fuctions as a extra arg
    // and we need to ignore it while calling external
    // functions
    return T_Call(T_Name( Temp_namedlabel(s) ), args->tail);
}

string F_getlabel(F_frame frame)
{
    return S_name(frame->name);
}

F_frame F_newFrame(Temp_label name, U_boolList formals)
{
    int count = 0; // the offset of the frame
    F_accessList p_alnew = NULL, p_alold = NULL;
    U_boolList itr = formals;
    F_frame p = checked_malloc(sizeof(*p));
    p->name = name;
    p->list = NULL;
    p->paramCount = 0;
    if (formals != NULL)
    {
    while(itr)
    {
        p_alnew = checked_malloc(sizeof(*p));
        p_alnew->next = NULL;

        // if it's escape or registers are used up, save it
        // in the frame
        if ( count / F_singleSize() > REG_FORPARAM )
        {
            p_alnew->data = InFrame(-count);
            count += F_singleSize();
            p->paramCount++;
        }
        else    // isn't escape and there's still reg to use
            p_alnew->data = InReg(Temp_newtemp());

        p_alnew->next = p_alold;
        p_alold = p_alnew;
        itr = itr->tail;
        }
    }
    p->list = p_alnew;
    p->paramBegin = p_alnew;
    p->length = count;
    return p;
};

Temp_label F_name(F_frame f)
{
    return f->name;
};

F_accessList F_formals(F_frame f)
{
    return f->paramBegin;
};

F_access F_allocLocal(F_frame f, bool escape, unsigned size)
{
    F_access result;
    if (escape)
    {
        result = InFrame(f->length);
        f->length += size;
        return result;
    }
    else
    {
        result = InReg(Temp_newtemp());
        return result;
    }
};

static F_access InFrame(int offset)
{
    F_access p = checked_malloc(sizeof(*p));
    p->kind = inFrame;
    p->u.offset = offset;
    return p;
};

static F_access InReg(Temp_temp reg)
{
    F_access p = checked_malloc(sizeof(*p));
    p->kind = inReg;
    p->u.reg = reg;
    return p;
};

static Temp_tempList returnSink = NULL;

AS_instrList F_procEntryExit2(AS_instrList body)
{
    /*
    if (!returnSink)
        returnSink = Temp_TempList( ZERO.
            Temp_tempList( RV,
            Temp_tempList( SP,
                calleeSaves)));
    return AS_splice(body, AS_InstrList(
        AS_Oper("", NULL, returnSink, NULL)), NULL);
    */
    assert(0);
    return NULL;
}
